package com.gator.spring.framework.context;

import com.gator.spring.framework.annotation.PABLO_Autowired;
import com.gator.spring.framework.annotation.PABLO_Controller;
import com.gator.spring.framework.annotation.PABLO_Service;
import com.gator.spring.framework.aop.PABLO_AopProxy;
import com.gator.spring.framework.aop.PABLO_CglibAopProxy;
import com.gator.spring.framework.aop.PABLO_JdkDynamicAopProxy;
import com.gator.spring.framework.aop.config.PABLO_AopConfig;
import com.gator.spring.framework.aop.support.PABLO_AdvisedSupport;
import com.gator.spring.framework.beans.PABLO_BeanWrapper;
import com.gator.spring.framework.beans.config.PABLO_BeanPostProcessor;
import com.gator.spring.framework.beans.factory.config.PABLO_BeanDefinition;
import com.gator.spring.framework.beans.support.PABLO_BeanDefinitionReader;
import com.gator.spring.framework.beans.support.PABLO_DefaultListableBeanFactory;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Authror PABLO
 * @Date 2022/5/7 11:25
 * @Desc 继承默认的IOC容器
 */
public class PABLO_ApplicationContext
        extends PABLO_DefaultListableBeanFactory {


    private String[] configLocations;
    //封装definition规范方法
    private PABLO_BeanDefinitionReader reader;

    //单例的IOC容器缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();
    //通用的IOC容器  缓存wrapper
    private Map<String, PABLO_BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<String, PABLO_BeanWrapper>();


    public PABLO_ApplicationContext(String... configLocations) {
        //只是加载配置文件并做任何处理
        this.configLocations = configLocations;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void refresh() throws Exception {
        //【1】创建bean工厂，当前实现类继承了PABLO_DefaultListableBeanFactory，省去了创建bean工厂步骤
        //【2】定位配置文件
        reader = new PABLO_BeanDefinitionReader(this.configLocations);
        //【3】扫描加载到内存的类，封装为definition规范
        List<PABLO_BeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
        //注册 将配置信息注册到beanDefinitionMap中，即放到bean工厂中
        doRegisterBeanDefinition(beanDefinitions);
        //【4】beanFactoryPostProcessor，这里未定义，可定义次增强步骤，不影响主流程
        //【5】调用beanFactoryPostProcessor，这里未定义，不影响主流程
        //【6】注册beanPostProcessor,我写在后面getBean方法中了
        //【7】初始化广播器 未定义，不影响主流程
        //【8】注册监听器 未定义，不影响主流程
        //【9】实例化所有非懒加载的类
        finishBeanFactoryInitialization();
    }

    private void finishBeanFactoryInitialization() {
        for (Map.Entry<String, PABLO_BeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            //beanFactory中的name，默认首字母小写
            String beanName = beanDefinitionEntry.getKey();
            //非懒加载 && 单例 直接getBean && 并且是非抽象的【这里未定义】
            if (!beanDefinitionEntry.getValue().isLazyInit()
                    && beanDefinitionEntry.getValue().isSingleton()) {
                try {
                    //判断是否实现了FactoryBean
                    if (isFactoryBean(beanName)) {
                        //特殊处理，如mybatis的mapperFactoryBean就是自定义的bean
                    } else {
                        getBean(beanName);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private boolean isFactoryBean(String beanName) {
        return false;
    }

    private void doRegisterBeanDefinition(List<PABLO_BeanDefinition> beanDefinitions) throws Exception {
        for (PABLO_BeanDefinition beanDefinition : beanDefinitions) {
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    /**
     * @Description: 通过class获取对象
     * @Author: PABLO
     * @Date: 2022/5/7 20:38
     * @Params: [clazz]
     * @Return: java.lang.Object
     **/
    public Object getBean(Class clazz) throws Exception {
        return getBean(clazz.getName());
    }

    /**
     * @Description: 根据beanName实例化并初始化对象
     * @Author: PABLO
     * @Date: 2022/5/7 17:27
     * @Params: [beanName 支持类的完全限定名 和 首字母小写类名]
     * @Return: java.lang.Object
     **/
    @Override
    public Object getBean(String beanName) throws Exception {

        //先考虑只有单例
        //存在直接拿
        if (this.singletonObjects.containsKey(beanName)) {
            return this.singletonObjects.get(beanName);
        }
        //1.实例化
        //获取对象的描述信息
        PABLO_BeanDefinition pablo_beanDefinition = beanDefinitionMap.get(beanName);

        //根据名称和对应的描述信息实例化bean
        Object instance = instantiateBean(beanName, pablo_beanDefinition);

        //将对象封装到beanWrapper中
        PABLO_BeanWrapper beanWrapper = new PABLO_BeanWrapper(instance);

        //存储IOC容器
        factoryBeanInstanceCache.put(beanName, beanWrapper);

        //DI注入  填充
        populateBean(beanName, new PABLO_BeanDefinition(), beanWrapper);
        //--------------------------------------------------------------------------
        //增强器
        PABLO_BeanPostProcessor postProcessor = new PABLO_BeanPostProcessor();
        //前置增强器
        postProcessor.postProcessBeforeInitialization(instance, beanName);
        //可设置init-Method
            //(1). 初始化前                	   postProcessBeforeInitialization()
            //(2). 执行构造方法之后                执行 @PostConstruct 的方法
            //(3). 所有属性赋初始化值之后           afterPropertiesSet()    该对象实现InitializingBean接口
            //(4). 初始化时              	       配置文件中指定的 init-method 方法
            //(5). 初始化后                	   postProcessAfterInitialization()
        //后置增强器
        postProcessor.postProcessAfterInitialization(instance, beanName);

        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }

    private void populateBean(String beanName, PABLO_BeanDefinition pablo_beanDefinition, PABLO_BeanWrapper beanWrapper) {
        Object instance = beanWrapper.getWrappedInstance();
        //获得实例对象的类 class com.gator.spring.executor.service.impl.xxxClass
        Class<?> clazz = beanWrapper.getWrappedClass();

        //判断只有加了注解的类，才执行依赖注入
        if (!(clazz.isAnnotationPresent(PABLO_Controller.class) || clazz.isAnnotationPresent(PABLO_Service.class)))
            return;
        //获得所有的fields
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //没有注入注解跳过
            if (!field.isAnnotationPresent(PABLO_Autowired.class)) continue;
            PABLO_Autowired autowired = field.getAnnotation(PABLO_Autowired.class);
            //获取注解上的value值
            String autowiredBeanName = autowired.value().trim();
            //“”默认类型名字
            if ("".equals(autowiredBeanName)) autowiredBeanName = field.getType().getName();
            //强制访问
            field.setAccessible(true);
            try {
                if (this.factoryBeanInstanceCache.get(autowiredBeanName) == null) {
                    continue;
                }
                //属性注入
                field.set(instance, this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
    }


    //beanName可能是小写名，也可能是全路径
    //我这里的逻辑默认创建的对象都是单例的，不涉及原型
    private Object instantiateBean(String beanName, PABLO_BeanDefinition pablo_beanDefinition) {
        Object instance = null;
        try {
            //获取该类完全限定名
            String className = pablo_beanDefinition.getBeanClassName();
            //反射创建
            Class<?> clazz = Class.forName(className);
            instance = clazz.newInstance();
            //加载代理配置
            PABLO_AdvisedSupport advisedSupport = loadAopConfig();
            advisedSupport.setTargetClass(clazz);
            advisedSupport.setTarget(instance);
            //实例化的类是否符合PointCut的规则的话，TRUE 创建代理对象
            //现在的匹配逻辑是com.gator.spring.executor.service.impl.*.*(..)中所有方法
            if (advisedSupport.pointCutMatch()) {
                instance = createProxy(advisedSupport).getProxy();
            }

            //this.singletonObjects.put(className, instance);
            //实现通过完全限定名和类名小写都能获取到对象
            this.singletonObjects.put(beanName, instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }

    /**
     * @Description: 根据对象信息创建代理对象
     * @Author: PABLO
     * @Date: 2022/5/9 14:38
     * @Params: [config]
     * @Return: com.gator.spring.framework.aop.PABLO_AopProxy
     **/
    private PABLO_AopProxy createProxy(PABLO_AdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        if (targetClass.getInterfaces().length > 0) {
            return new PABLO_JdkDynamicAopProxy(config);
        }
        return new PABLO_CglibAopProxy(config);
    }

    private PABLO_AdvisedSupport loadAopConfig() {
        PABLO_AopConfig config = new PABLO_AopConfig();
        //加载配置文件
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAspectAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectAfterThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectAfterThrowingName"));
        return new PABLO_AdvisedSupport(config);
    }
}
